//#include <fuse.h>
#include <stdio.h>
#include <stdint.h>//uintN_t N bits, http://linux.die.net/man/3/uint32_t
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>//manejar errores
#include <unistd.h>
#include <fcntl.h>//posix manejar archivos
#include <dirent.h>//posix manejar directorios
#include <sys/stat.h>
#include "operacionesSectores.h"
#include "estructuras.h"
//#include "operacionesFUSE.h"
#include "imprimir.h"
#include "funcionesBasicas.h"
#include "utils.h"
#include "entriesLFN.h"
#include "capaDireccionamiento.h"
#include <malloc.h>

const uint32_t  clusterFin[8] = {0x0FFFFFF8, 0x0FFFFFF9, 0x0FFFFFFA, 0x0FFFFFFB, 0x0FFFFFFC, 0x0FFFFFFD, 0x0FFFFFFE, 0x0FFFFFFF};


//****************************************************************************************************

void leerFatRegion(uint32_t);
FatRegion leerFatRegionCompleta(const char *);
void leerContenidoDirectorio(uint32_t,PtrListaLFN *);

//*************************************************************************************


FatRegion leerFatRegionCompleta(const char *path){

        uint16_t indice = 0;
        uint16_t indiceMaxSgte = 128;
        uint32_t numSector;
        uint16_t numByte = 0;
        uint32_t sectorFinalFat = SECTORS_PER_FAT + RESERVED_SECTOR_COUNT;
        char contenedor[SIZE_SECTOR];
        FatRegion tabla;


        int32_t descriptor = abrirArchivo(path);

        for( numSector = RESERVED_SECTOR_COUNT - 1; numSector < sectorFinalFat; numSector++){

        		char *buffer = leerSector(numSector,contenedor,descriptor);
        		while (indice < indiceMaxSgte){

                		memcpy(&tabla.entradasFat[indice],&buffer[numByte],4);
                		numByte = numByte + 4;
                		if (tabla.entradasFat[indice] == 0){ vectorBitMap.bitmap[indice] = 0;}
                		else { vectorBitMap.bitmap[indice] = 1;}
                		indice++;
                		if (indice == indiceMaxSgte){numByte = 0;}//cuando llego a leer el sector completo vuelve a leer el sector sgte
                }
        		indiceMaxSgte = indiceMaxSgte + 128;
        }
        close(descriptor);
        return tabla;
}

void cargarCacheFat(uint32_t nroBloque){

		capaDeDireccionamientoAsectoresLectura(nroBloque);
		cacheFat = capaDir;
}


void leerFatRegion(uint32_t nroBloque){

		if ( (nroBloque < RESERVED_SECTOR_COUNT/SIZE_BLOCK)||(nroBloque > DIR_DATA_REGION/SIZE_BLOCK)){
			perror("el bloque no pertenece a la tabla FAT");
			exit(1);
		} else { cargarCacheFat(nroBloque); actualizarBitMap(nroBloque);}
}

Sector leerBootSector(){
        //por socket pedirle al praid o pdd que lean el sector 0

        Sector bootSector;

        inicializarCache(&capaDir);//
        capaDeDireccionamientoAsectoresLectura(0);
        bootSector = capaDir.sectores[0];

        imprimirSector(bootSector.bytes);
        return bootSector;
}

void armarArrayconNombresDeDirectorio(const char *cadena,char** directorios){

	if( directorios == NULL){
		printf("Error en la asignación de memoria en armarArrayconNombresDeDirectorio");
		exit(1);
	}

	uint8_t nroCaracter = 0;
	nroCaracter = 0;
	uint8_t nroDirectorio;
	nroDirectorio = 0;

	while((cadena[nroCaracter] != '\0')&&(cadena[nroCaracter] != '/')){
		char buffer[13];
		memset(buffer,0,13);
		uint8_t caracterBuffer = 0;
		while((cadena[nroCaracter] != '/')&&(cadena[nroCaracter] != '\0')){
			buffer[caracterBuffer] = cadena[nroCaracter];
			nroCaracter++;
			caracterBuffer++;
		}
		directorios[nroDirectorio] = malloc(strlen(buffer)+1);
		strcpy(directorios[nroDirectorio],buffer);
		if( cadena[nroCaracter] != '\0'){
			nroDirectorio++;
			nroCaracter++;
		}
	}
}

void leerContenidoDirectorio(uint32_t nroCluster, PtrListaLFN *ptrLFN){//cluster donde empieza el directorio

	EntradaFat entradaFat;
	uint32_t clusterSgte;

	entradaFat = calcularUbicacionDeEntradaFatDel(nroCluster);
	capaDeDireccionamientoAsectoresLectura(entradaFat.bloque);
	clusterSgte = obtenerClusterSiguienteEnLaFat(entradaFat);

	capaDeDireccionamientoAsectoresLectura( convertirClusterAbloque(nroCluster));
	filtrarEntradas(ptrLFN);
	if( clusterSgte == 0){return;}

	uint8_t indiceArray = 0;

	for( indiceArray = 0; indiceArray < 8; indiceArray++){
		if(clusterSgte == clusterFin[indiceArray]){break;}
	}

	if( clusterSgte != clusterFin[indiceArray]){
		leerContenidoDirectorio(clusterSgte,ptrLFN);
	}

	return;
}

void listarDirectorios(char *ruta, PtrListaLFN *ptrLFN){

	uint8_t nroCaracter = 0;
	uint32_t cantDirectorios = 0;
	char freeDirectorios;//"semáforo" 0 falso(no se puede hacer free) 1 verdadero

	while(ruta[nroCaracter] != '\0'){
		if(ruta[nroCaracter] == '/'){cantDirectorios++;}
		nroCaracter++;
	}

	char ** arrayDirectorios = NULL;
	arrayDirectorios = calloc(cantDirectorios,sizeof(char *));
	freeDirectorios = 1;
	armarArrayconNombresDeDirectorio(ruta,arrayDirectorios);
	*ptrLFN = NULL;
	uint8_t nroDir = 0;

	if(*arrayDirectorios[nroDir] == '.'){
		nroDir++;
		capaDeDireccionamientoAsectoresLectura(convertirClusterAbloque(2));
		//printf("Directorio: %s \n",arrayDirectorios[cantDirectorios-1]);
		if( arrayDirectorios[nroDir] == NULL){
			leerContenidoDirectorio(2,ptrLFN);
			if( freeDirectorios == 1){
				for(uint8_t indArray = 0; indArray < cantDirectorios; indArray++){free(arrayDirectorios[indArray]);}
				free(arrayDirectorios);
				freeDirectorios = 0;
			}
			return;}
	}

	if(arrayDirectorios[nroDir] != NULL){
		if(*arrayDirectorios[0] != '.'){nroDir++;}
		UbicacionCache ubicacionCacheDir;
		ubicacionCacheDir = buscarLFNenCacheDireccionamiento(arrayDirectorios[nroDir]);
		if (ubicacionCacheDir.nroByte != 300){ //300 es el error de la funcion
			uint32_t nroCluster;

			nroCluster = obtenerPrimerClusterEnFat(ubicacionCacheDir);
			capaDeDireccionamientoAsectoresLectura(convertirClusterAbloque(nroCluster));

			if(arrayDirectorios[nroDir+1] == NULL){
				leerContenidoDirectorio(nroCluster,ptrLFN);
			} else{ //armar cadena con los directorios que quedan en el array
				char * cadena = NULL;
				cadena = malloc(sizeof(char));
				uint8_t caracter = 0;
				for( uint8_t indice = nroDir; indice < cantDirectorios; indice++){
					strcpy(&cadena[caracter],arrayDirectorios[indice]);
					caracter = caracter + strlen(arrayDirectorios[indice]);
					cadena[caracter] = '/';
					caracter++;
				}
				cadena[caracter] = '\0';
				if( freeDirectorios == 1){
					for(uint8_t indArray = 0; indArray < cantDirectorios; indArray++){free(arrayDirectorios[indArray]);}
					free(arrayDirectorios);
					freeDirectorios = 0;
				}
				listarDirectorios(cadena,ptrLFN);
			}
		}
	}
	if( freeDirectorios == 1){
		for(uint8_t indArray = 0; indArray < cantDirectorios; indArray++){free(arrayDirectorios[indArray]);}
		free(arrayDirectorios);
		freeDirectorios = 0;
	}
}


UbicacionCache ubicarArchivo(const char *path){

	uint8_t nroCaracter = 0;
	uint32_t cantDirectorios = 0;
	//char freeDirectorios;//"semáforo" 0 falso(no se puede hacer free) 1 verdadero

	while(path[nroCaracter] != '\0'){
		if(path[nroCaracter] == '/'){cantDirectorios++;}
		nroCaracter++;
	}

	char ** arrayDirectorios = NULL;
	arrayDirectorios = calloc(cantDirectorios+1,sizeof(char *));//+1 por el nombre del archivo
	armarArrayconNombresDeDirectorio(path,arrayDirectorios);
	char *buffer = NULL;
	buffer = calloc(strlen(path)-strlen(arrayDirectorios[cantDirectorios]),sizeof(char));
	strncat(buffer,path,strlen(path)-strlen(arrayDirectorios[cantDirectorios]));
	PtrListaLFN ptr = NULL;
	listarDirectorios(buffer,&ptr);
	UbicacionCache ubicacionLfnArchivo;
	ubicacionLfnArchivo = buscarLFNenCacheDireccionamiento(arrayDirectorios[cantDirectorios]);
	free(arrayDirectorios);
	free(buffer);
	return ubicacionLfnArchivo;
}

void obtenerSizeFile(UbicacionCache entrada, uint32_t *sizeFile){
	memcpy(sizeFile,&capaDir.sectores[entrada.nroSectorEntradaSgte].bytes[(entrada.nroByteEntradaSgte) + 0x1C],4);
}

aux calcularSectorDadoOffset(uint32_t offset, uint32_t bloqueInicio, uint32_t fileSize){//uint32_t bytes, uint32_t bloqueInicio){

	uint8_t bloque;
		uint8_t sector;
		uint16_t byte;
		uint32_t cantBytesLeidos = 0;//bloqueInicio*SIZE_BLOCK*SIZE_SECTOR;
		aux ubicacion;

		if( offset < (SIZE_BLOCK * SIZE_SECTOR)){
			for(sector = 0; sector < SIZE_BLOCK; sector++){

							for( byte = 0; byte < SIZE_SECTOR; byte++){
								if( cantBytesLeidos == offset){
									ubicacion.bloque = 0;
									ubicacion.sector = sector;
									ubicacion.byte = byte;
									ubicacion.error = 0;
									return ubicacion;
								}
								cantBytesLeidos++;
							}
						}
		} else{
			for( bloque = 0; bloque < (fileSize/SIZE_BLOCK); bloque++){

				for(sector = 0; sector < SIZE_BLOCK; sector++){

					for( byte = 0; byte < SIZE_SECTOR; byte++){
						if( cantBytesLeidos == offset){
							ubicacion.bloque = bloque;
							ubicacion.sector = sector;
							ubicacion.byte = byte;
							ubicacion.error = 0;
							return ubicacion;
						}
						cantBytesLeidos++;
					}
				}
			}
		}
		ubicacion.error = ERROR_BYTES_NOT;
		return ubicacion;
}

int32_t readFile(const char *path, char *dataBuffer, size_t dataBufferSize, off_t offsetFile){

	//int32_t ERROR_OFFST_DATASIZE = -1;
	int32_t ERROR_UBICACION_ARCHIVO = -2;
	int32_t ERROR_OFFSET_SOBREPASA_FILESIZE = -3;

	UbicacionCache ubicacionLfnArchivo;
	ubicacionLfnArchivo = ubicarArchivo(path);

	if( ubicacionLfnArchivo.nroSector != 300) {

		uint8_t indiceArray;
		char esUltimoCluster = 0;
		uint32_t sizeFile;
		uint32_t firstCluster;
		UbicacionCache entradaCache;
		int32_t bytesLeidos = 0;
		aux ubicacionOffset;
		uint32_t bloqueLeido = 0;

		entradaCache = ubicacionLfnArchivo;
		firstCluster = obtenerPrimerClusterEnFat(entradaCache);
		obtenerSizeFile(entradaCache,&sizeFile);
		//printf("tam archivo: %d \n",sizeFile);//**********************
		capaDeDireccionamientoAsectoresLectura(convertirClusterAbloque(firstCluster));

		if(offsetFile > sizeFile){
			printf("\nEstá intentando leer fuera del archivo\n");
			return ERROR_OFFSET_SOBREPASA_FILESIZE;
		}

		ubicacionOffset = calcularSectorDadoOffset(offsetFile,bloqueLeido,sizeFile);//calcularSectorDadoOffset(offsetFile,bloqueLeido);

		if (ubicacionOffset.error != ERROR_BYTES_NOT){
			if(ubicacionOffset.bloque == bloqueLeido){
				guardarDatosApartirDe(&bytesLeidos,offsetFile,dataBuffer,sizeFile,ubicacionOffset,dataBufferSize);
				ubicacionOffset.bloque++;
				ubicacionOffset.byte = 0;
				ubicacionOffset.sector = 0;
				if((bytesLeidos == dataBufferSize)||((offsetFile+bytesLeidos) == sizeFile)){
					return bytesLeidos;
				}
			}
		}
		EntradaFat entradaFatFirstCluster;
		uint32_t nroClusSgte;

		entradaFatFirstCluster = calcularUbicacionDeEntradaFatDel(firstCluster);
		cargarCacheFat(entradaFatFirstCluster.bloque);
		nroClusSgte = obtenerClusterSiguienteEnLaFat(entradaFatFirstCluster);

		while(esUltimoCluster == 0){
			for( indiceArray = 0; indiceArray < 8; indiceArray++){
				if(nroClusSgte == clusterFin[indiceArray]){esUltimoCluster = 1; break;}
			}

			if( nroClusSgte != clusterFin[indiceArray]){

				capaDeDireccionamientoAsectoresLectura(convertirClusterAbloque(nroClusSgte));
				bloqueLeido++;
				if (ubicacionOffset.error != ERROR_BYTES_NOT){
					if(ubicacionOffset.bloque == bloqueLeido){
					guardarDatosApartirDe(&bytesLeidos,offsetFile,dataBuffer,sizeFile,ubicacionOffset,dataBufferSize);
					ubicacionOffset.byte = 0;
					ubicacionOffset.sector = 0;
						if((bytesLeidos == dataBufferSize)||((offsetFile+bytesLeidos) == sizeFile)){
							return bytesLeidos;
						}
					}
				}
				EntradaFat entradaFatNextCluster;

				entradaFatNextCluster = calcularUbicacionDeEntradaFatDel(nroClusSgte);
				cargarCacheFat(entradaFatNextCluster.bloque);
				nroClusSgte = obtenerClusterSiguienteEnLaFat(entradaFatNextCluster);
			}
		}
		return bytesLeidos;
	}
	return ERROR_UBICACION_ARCHIVO;
}

int32_t renameFile (const char *path, const char *newName){

	UbicacionCache ubicacionLfnArchivo;
	ubicacionLfnArchivo = ubicarArchivo(path);

	if( ubicacionLfnArchivo.nroSector != 300) {

		uint8_t entrada[32];
		LFN entradaLfn;
		uint16_t newNameUTF16[13];
		memset(newNameUTF16,0,13);

		memcpy(entrada,&capaDir.sectores[ubicacionLfnArchivo.nroSector].bytes[ubicacionLfnArchivo.nroByte],SIZE_ENTRADA_DATA_REGION);
		castEntryToLfn(entrada,&entradaLfn);
		armarNombreLFNaUTF16(newName,newNameUTF16);
		modificarLfn(&entradaLfn,newNameUTF16);
		castLfnToEntry(entrada,&entradaLfn);
		memcpy(&capaDir.sectores[ubicacionLfnArchivo.nroSector].bytes[ubicacionLfnArchivo.nroByte],entrada,SIZE_ENTRADA_DATA_REGION);
		for( int i = 0; i < 13; i++){
		printf("utf16: %c \n",newNameUTF16[i]);
		}
		capaDeDireccionamientoAsectoresEscritura();
		return SUCCESSFUL;
	} else{ return ERROR_LOCATION_FILE;}
}


int main(int argc, char *argv[]) {

//******************************** LISTAR DIRECTORIOS *******************************************

	inicializarCache(&capaDir); inicializarCache(&cacheFat);

//Prueba LISTAR DIRECTORIOS e imprimir ******************
/*
	PtrListaLFN ptr = NULL;
	listarDirectorios("./",&ptr);
	imprimirListaLFNs(ptr);
	listarDirectorios("./carpeta2/subcarpeta2/",&ptr);
	imprimirListaLFNs(ptr);
*/

//PRUEBA READfILE **********
/*
	//char *path = "./carpeta2/subcarpeta2/archivo.txt";
	char *path = "./carpeta2/subcarpeta2/grande.txt";

	char *buffer = NULL;
	buffer = calloc(4096,sizeof(char));
	int32_t i;
	for(i = 0; i < 4883; i= i + 4096){
		int32_t bytesLeidos = readFile(path,buffer,4096,i);
		//printf("cantidad de bytes leidos por read: %d\n",bytesLeidos);
		printf("%s",buffer);
		printf("\n");
	}
*/

//PRUEBA RENAME FILE **********
/*
	char *path = "./carpeta2/subcarpeta2/archivo.txt";
	renameFile(path,"yo.txt");
*/
//PRUEBA RENAME DIRECTORY **********
/*
	char *path = "./carpeta2/subcarpeta";
	renameFile(path,"su2bca2peta2");//directorios aceptan 8.3 entonces se cortan, modifican el DOS name
*/
	return 0;
}

